/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.servlet;

import cn.easyplatform.lang.Files;
import cn.easyplatform.lang.Nums;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.SimpleRequestMessage;
import cn.easyplatform.messages.vos.EnvVo;
import cn.easyplatform.messages.vos.GuestTaskVo;
import cn.easyplatform.messages.vos.LoginVo;
import cn.easyplatform.spi.service.ApiService;
import cn.easyplatform.spi.service.IdentityService;
import cn.easyplatform.type.*;
import cn.easyplatform.web.WebApps;
import cn.easyplatform.web.contexts.Contexts;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.utils.CookieUtils;
import cn.easyplatform.web.utils.ExtUtils;
import cn.easyplatform.web.utils.WebUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.util.resource.Labels;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class EasyPlatformFilter implements Filter {

    private final static Logger LOG = LoggerFactory.getLogger(EasyPlatformFilter.class);

    private String _header;

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.Filter#destroy()
     */
    @Override
    public void destroy() {
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
     * javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
                         FilterChain chain) throws IOException, ServletException {
        try {
            final HttpServletRequest req = (HttpServletRequest) arg0;
            String url = req.getRequestURI().substring(
                    req.getContextPath().length());
            if (url.startsWith("/zkau") || url.startsWith("/zkwm")) {
                chain.doFilter(arg0, arg1);
            } else if (url.indexOf(".go") > 0) {
                HttpServletResponse resp = (HttpServletResponse) arg1;
                EnvVo env = (EnvVo) req.getSession().getAttribute(
                        Contexts.PLATFORM_APP_ENV);
                if (url.startsWith("/login")) {
                    String appContext = req.getParameter("app");
                    if (appContext == null) {
                        String val = CookieUtils.getCookieValue(req, "zk-project");
                        if (Strings.isBlank(val))
                            appContext = "";
                        else
                            appContext = val;
                    }
                    String portlet = req.getParameter("portlet");
                    Map<String, Object> variables = new HashMap<String, Object>();
                    Enumeration<String> itr = req.getParameterNames();
                    while (itr.hasMoreElements()) {
                        String name = itr.nextElement();
                        int no = Nums.toInt(name, 0);
                        if (no > 760 && no < 800)// 只有这个范围内才有效
                            variables.put(name, req.getParameter(name));
                    }
                    if (env == null || !appContext.equals(env.getAppContext())) {
                        if (env != null) {
                            req.getSession().removeAttribute(
                                    Contexts.PLATFORM_APP_ENV);
                            env = null;
                        }
                        env = new EnvVo();
                        env.setAppContext(appContext);
                        env.setDeviceType(ExtUtils.getDeviceType(req));
                        env.setPortlet(portlet);
                        env.setVariables(variables);
                        SimpleRequestMessage request = new SimpleRequestMessage(env);
                        IdentityService uc = ServiceLocator
                                .lookup(IdentityService.class);
                        IResponseMessage<?> message = uc.getAppEnv(request);
                        if (!message.isSuccess()) {
                            if (WebApps.me().get("MOD_READY") != null) {//未就绪
                                process(req, resp, Files.read(("web/pages/unset.zul")), false);
                            } else
                                process(req, resp, Files.read(("web/pages/error.zul")), false);
                        } else {
                            env = (EnvVo) message.getBody();
                            env.setLogoutUrl(url + (req.getQueryString() == null ? "" : "?" + req.getQueryString()));
                            WebUtils.createAppPath(env);
                            req.getSession().setAttribute(Constants.SESSION_ID, env.getSessionId());
                            req.getSession().setAttribute(
                                    Contexts.PLATFORM_APP_ENV, env);
                            process(req, resp, env.getLoginPage(), true);
                        }
                    } else if (env.getMainPage() != null) {
                        LoginVo loginVo = (LoginVo) req.getSession().getAttribute(Contexts.PLATFORM_USER);
                        if (loginVo == null || loginVo.getType() >= UserType.TYPE_OAUTH) {
                            env.setPortlet(portlet);
                            env.setVariables(variables);
                            env.setLogoutUrl(url + (req.getQueryString() == null ? "" : "?" + req.getQueryString()));
                            process(req, resp, env.getLoginPage(), true);
                        } else
                            process(req, resp, env.getMainPage(), true);
                    } else {
                        env.setPortlet(portlet);
                        env.setVariables(variables);
                        env.setLogoutUrl(url + (req.getQueryString() == null ? "" : "?" + req.getQueryString()));
                        process(req, resp, env.getLoginPage(), true);
                    }
                } else if (url.startsWith("/main")) {
                    if (env == null) {
                        resp.sendRedirect(StringUtils.replaceOnce(req.getRequestURI(), "/main", "/login") + (req.getQueryString() == null ? "" : "?" + req.getQueryString()));
                    } else if (env.getMainPage() == null)
                        process(req, resp, env.getLoginPage(), true);
                    else
                        process(req, resp, env.getMainPage(), true);
                } else if (url.startsWith("/page")) {//匿名访问
                    String serviceId = req.getParameter("app");
                    String taskId = req.getParameter("tid");
                    if (Strings.isBlank(serviceId) || Strings.isBlank(taskId)) {
                        req.getSession().setAttribute("_resp_", Labels.getLabel("api.url.401"));
                        ApiGatewayServlet.processZul(req, resp);
                    } else {
                        doAnonymous(req, resp, serviceId, taskId);
                    }
                } else
                    process(req, (HttpServletResponse) arg1, Files.read(("web/pages/error.zul")), false);

            } else if (url.startsWith("/portal")) {//外部连接
                HttpServletResponse resp = (HttpServletResponse) arg1;
                processZul(req, resp, chain);
            } else if (url.startsWith("/setup")) {//外部连接
                HttpServletResponse resp = (HttpServletResponse) arg1;
                process(req, resp, Files.read("web/pages/setup.zul"), false);
            } else if (!url.endsWith("zkcomet"))
                chain.doFilter(arg0, arg1);
        } catch (Exception e) {
            LOG.error("process:", e);
            ((HttpServletResponse) arg1).sendError(404);
        }
    }

    /**
     * 匿名登陆
     *
     * @param req
     * @param resp
     * @param serviceId
     * @param taskId
     * @throws IOException
     */
    private void doAnonymous(HttpServletRequest req,
                             HttpServletResponse resp, String serviceId, String taskId) throws IOException {
        Enumeration<String> ems = req.getParameterNames();
        List<FieldVo> args = new ArrayList<>();
        while (ems.hasMoreElements()) {
            String name = ems.nextElement();
            if (!"tid".equals(name) && !"app".equals(name))
                args.add(new FieldVo(name, FieldType.VARCHAR, req.getParameter(name)));
        }
        GuestTaskVo tv = new GuestTaskVo(taskId, ExtUtils.getDeviceType(req), serviceId);
        tv.setVariables(args);
        tv.setIp(WebApps.getRemoteAddr(req));
        SimpleRequestMessage requestMessage = new SimpleRequestMessage(tv);
        requestMessage.setSessionId((String) req.getSession().getAttribute(Constants.SESSION_ID));
        ApiService as = ServiceLocator
                .lookup(ApiService.class);
        IResponseMessage<?> rs = as.anonymous(requestMessage);
        req.getSession().setAttribute("_resp_", rs);
        if (rs.isSuccess()) {
            if (req.getSession().getAttribute(Contexts.PLATFORM_USER) == null) {
                LoginVo vo = new LoginVo();
                vo.setIp(WebApps.getRemoteAddr(req));
                vo.setLocale(req.getLocale().toString());
                vo.setType(UserType.TYPE_ANONYMOUS);
                vo.setName("guest");
                req.getSession().setAttribute(Contexts.PLATFORM_USER, vo);
            }
        }
        ApiGatewayServlet.processZul(req, resp);
    }

    private void processZul(HttpServletRequest request,
                            HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        if (request.getParameter("preview") != null) {
            String name = request.getParameter("preview");
            String xul = (String) request.getSession().getAttribute(name);
            if (!Strings.isBlank(xul)) {
                process(request, response, xul, true);
                request.getSession().removeAttribute(name);
            } else
                chain.doFilter(request, response);
        } else
            chain.doFilter(request, response);
    }

    private void process(HttpServletRequest request,
                         HttpServletResponse response, String xul, boolean includeHead)
            throws IOException {
        if (includeHead) {
            StringBuilder sb = new StringBuilder(_header);
            sb.append(xul);
            xul = sb.toString();
            sb = null;
        }
        WebUtils.processZul(request, response, xul);
    }

    @Override
    public void init(FilterConfig config) {
        _header = Files.read(("web/pages/header"));
    }
}
